{
"cells": [
{
"cell_type": "markdown",
"id": "dc15734d-8749-49ae-be80-c3392e9699e1",
"metadata": {
"raw_mimetype": "text/restructuredtext"
},
"source": [
"# Time Evolution\n",
"\n",
"Time evolutions in `quimb` are handled by the class {class}`~quimb.evo.Evolution`, which is initialized with a starting state and hamiltonian.\n",
"\n",
"## Basic Usage\n",
"\n",
"Set up the {class}`~quimb.evo.Evolution` object with a initial state and hamiltonian."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "1330c133-a456-4273-b4e9-0f5edab0040d",
"metadata": {},
"outputs": [],
"source": [
"%config InlineBackend.figure_formats = ['svg']\n",
"import quimb as qu\n",
"\n",
"p0 = qu.rand_ket(2**10)\n",
"h = qu.ham_heis(10, sparse=True)\n",
"evo = qu.Evolution(p0, h)"
]
},
{
"cell_type": "markdown",
"id": "bad44ff5-2dcc-43e1-912c-aff549e44221",
"metadata": {},
"source": [
"Update it in a single shot to a new time and get the state,"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "2edbe41e-48c8-4e69-b393-f3dbce606845",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[[ 0.027396-0.00772j ]\n",
" [ 0.00786 -0.019236j]\n",
" [ 0.00165 +0.021386j]\n",
" ...\n",
" [-0.04785 -0.028168j]\n",
" [ 0.006677+0.038223j]\n",
" [ 0.011585-0.006924j]]"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evo.update_to(1)\n",
"evo.pt"
]
},
{
"cell_type": "markdown",
"id": "b5764132-0502-4650-a401-0c83f4cf23d6",
"metadata": {},
"source": [
"Lazily generate the state at multiple times:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "2b138fbe-6ac1-4ceb-beb8-6b5c2e3e0184",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.0003167209578964015\n",
"0.0033296109901839177\n",
"0.0012326190125073922\n"
]
}
],
"source": [
"for pt in evo.at_times([2, 3, 4]):\n",
" print(qu.expec(pt, p0))"
]
},
{
"cell_type": "markdown",
"id": "3c8ea54a-ac64-4afd-81ef-7e87608aaaef",
"metadata": {
"raw_mimetype": "text/restructuredtext"
},
"source": [
"## Methods of Updating\n",
"\n",
"There are three methods of updating the state:\n",
"\n",
"> - `Evolution(..., method='integrate')`: use definite integration.\n",
"> Get system at each time step, only need action of Hamiltonian on\n",
"> state. Generally efficient. For pure and mixed states. The\n",
"> additional option `int_small_step={False, True}` determines\n",
"> whether a low or high order adaptive stepping scheme is used,\n",
"> giving naturally smaller or larger times steps. See\n",
"> {class}`scipy.integrate.ode` for details, `False` corresponds\n",
"> to `\"dop853\"`, `True` to `\"dopri5\"`.\n",
"> - `Evolution(..., method='solve')`. Diagonalize the hamiltonian,\n",
"> which once done, allows quickly updating to arbitrary times.\n",
"> Supports pure and mixed states, recomended for small systems.\n",
"> - `Evolution(..., method='expm')`: compute the evolved state\n",
"> using the action of the matrix exponential in a 'single shot'\n",
"> style. Only needs action of Hamiltonian, for very large systems\n",
"> can use distributed MPI. Only for pure states.\n",
"\n",
"## Computing on the fly\n",
"\n",
"Sometimes, if integrating, it is best to just query the state at time-steps chosen dynamically by the adaptive scheme. This is achieved using the `compute` keyword supplied to `Evolution`. It can also just be a convenient way to set up calculations as well:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "0dc964f7-5ac2-44ad-aaeb-320609078861",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|##########| 100/100 [00:00<00:00, 20516.06%/s]\n"
]
}
],
"source": [
"p0 = qu.rand_product_state(10)\n",
"h = qu.ham_heis(10, sparse=True)\n",
"\n",
"dims = [2] * 10\n",
"sysa, sysb = (0, 1), (2, 3)\n",
"\n",
"\n",
"def calc_t_and_logneg(t, pt):\n",
" ln = qu.logneg_subsys(pt, dims, sysa, sysb)\n",
" return t, ln\n",
"\n",
"\n",
"evo = qu.Evolution(p0, h, compute=calc_t_and_logneg, progbar=True)\n",
"evo.update_to(1)\n",
"\n",
"ts, lns = zip(*evo.results)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "2387b525-54e8-4dca-ae42-8d5eca0b9916",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(0.0,\n",
" 0.2494153162899183,\n",
" 0.4809058736983094,\n",
" 0.7202389885285744,\n",
" 0.9908548077660357,\n",
" 1.0)"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ts"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "0908d890-675e-449e-aa3c-a82c7d03aab8",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(0.0,\n",
" 0.07922473340252047,\n",
" 0.19984430125848626,\n",
" 0.3726546674413778,\n",
" 0.5937245957372282,\n",
" 0.6011504266800122)"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"lns"
]
},
{
"cell_type": "markdown",
"id": "2636ba38-c42b-4f56-b495-e968c8d8d464",
"metadata": {
"raw_mimetype": "text/restructuredtext"
},
"source": [
"If a dict of callables is supplied to `compute`, (each should take two arguments, the time, and the state, as above), `Evolution.results` will itself be a dictionary containing the results of each function at each time step, under the respective key. This can be more convenient:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "55c0a8d7-ad7b-43ff-8b6d-ff60560001f6",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|##########| 100/100 [00:00<00:00, 22323.19%/s]\n"
]
}
],
"source": [
"def calc_t(t, _):\n",
" return t\n",
"\n",
"\n",
"def calc_logneg(_, pt):\n",
" return qu.logneg_subsys(pt, [2] * 10, 0, 1)\n",
"\n",
"\n",
"evo = qu.Evolution(\n",
" p0, h, compute={\"t\": calc_t, \"ln\": calc_logneg}, progbar=True\n",
")\n",
"evo.update_to(1)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "972f485b-64c4-46e5-86b4-16cbd93d81a2",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'t': [0.0,\n",
" 0.2494153162899183,\n",
" 0.4809058736983094,\n",
" 0.7202389885285744,\n",
" 0.9908548077660357,\n",
" 1.0],\n",
" 'ln': [0.0,\n",
" 0.2670014309042237,\n",
" 0.4550737777089945,\n",
" 0.5894993607543968,\n",
" 0.6676819999827515,\n",
" 0.66894221219201]}"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evo.results"
]
},
{
"cell_type": "markdown",
"id": "d904d5df-f633-4b96-98cd-234f326e98fa",
"metadata": {
"raw_mimetype": "text/restructuredtext"
},
"source": [
"(time-dependent-evolution)=\n",
"\n",
"## Time-Dependent Evolutions\n",
"\n",
"If you are using `method='integrate'` you can supply a callable to `ham` to evolve the state with a time dependent Hamiltonian. It should take a single argument `t` and return the Hamiltonian at the time. It probably makes sense to use a custom class here to avoid reconstructing as much of the Hamiltonian as possible at each step.\n",
"\n",
"Here we'll evolve the Neel state:\n",
"\n",
"$$\n",
"| \\psi(0) \\rangle = | \\uparrow \\downarrow \\uparrow \\downarrow \\uparrow \\ldots \\rangle\n",
"$$\n",
"\n",
"with the Hamiltonian:\n",
"\n",
"$$\n",
"H(t) = \\sum_{i = 0}^{L - 1} S^Z_{i} S^Z_{i + 1} + \\cos(t) \\sum_{i}^{L} S^X_i\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "c64f70b8-a689-4fc9-ac49-eb90e2c5921c",
"metadata": {},
"outputs": [],
"source": [
"class MyTimeDepIsingHam:\n",
" def __init__(self, L):\n",
" self.h_interaction = qu.ham_ising(\n",
" L, sparse=True, jz=1.0, bx=0.0, cyclic=False\n",
" )\n",
" self.h_field = qu.ham_ising(\n",
" L, sparse=True, jz=0.0, bx=1.0, cyclic=False\n",
" )\n",
"\n",
" def __call__(self, t):\n",
" return self.h_interaction + qu.cos(t) * self.h_field"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "1480b979-aa44-4726-b01d-830f767613e4",
"metadata": {},
"outputs": [],
"source": [
"L = 16\n",
"\n",
"# our initial state\n",
"psi0 = qu.neel_state(L)\n",
"\n",
"# instantiate the ham object, it's __call__ method will be used by Evolution\n",
"fn_ham_t = MyTimeDepIsingHam(L)"
]
},
{
"cell_type": "markdown",
"id": "0a86b6c6-8a02-44b0-ac25-85f074af69c5",
"metadata": {},
"source": [
"We still want to compute some properties during the evolution:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "67a204ea-4f14-435d-b68d-746756ad77e1",
"metadata": {},
"outputs": [],
"source": [
"compute = {\n",
" \"time\": lambda t, p: t,\n",
" \"entropy\": lambda t, p: qu.entropy_subsys(\n",
" p, dims=[2] * L, sysa=range(L // 2)\n",
" ),\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "256af3bb-af91-4139-bfa9-68f6703403c9",
"metadata": {},
"source": [
"Now we set up the evolution object again:"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "e2608200-f85e-461f-b347-116b8b464d24",
"metadata": {},
"outputs": [],
"source": [
"evo = qu.Evolution(psi0, fn_ham_t, progbar=True, compute=compute)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "d26978f4-1fb2-4f8a-b0d6-1338fff320f6",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|##########| 100/100 [00:13<00:00, 7.64%/s]\n"
]
}
],
"source": [
"evo.update_to(10)"
]
},
{
"cell_type": "markdown",
"id": "4735d921-676f-4db6-8dc8-573b42f44f3f",
"metadata": {},
"source": [
"We can plot the half chain entropy that we computed on the fly:"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "e4d8cb4e-16ab-47dd-8a01-e0c91836b5dc",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"[]"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"qu.plot(evo.results[\"time\"], evo.results[\"entropy\"])"
]
},
{
"cell_type": "markdown",
"id": "59aa8c6a-e5ee-4720-a132-f54966e3a9d7",
"metadata": {},
"source": [
"Or we can use the final state:"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "81f8058b-7128-42f5-9e70-56699dc6ff32",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"np.float64(0.003302180752068547)"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"qu.fidelity(psi0, evo.pt)"
]
}
],
"metadata": {
"celltoolbar": "Raw Cell Format",
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}